namespace DanMuHelper.Tools
{
    using UnityEngine;
    using System.Collections.Generic;
    using Unity.Mathematics;

    public static partial class MathUtil
    {
        /// <summary>
        ///   计算距离，忽略Y轴
        /// </summary>
        public static float DistanceXZ(ref Vector3 a, ref Vector3 b)
        {
            float dx = a.x - b.x;
            float dz = a.z - b.z;
            return Mathf.Sqrt(dx * dx + dz * dz);
        }

        /// <summary>
        ///   计算距离，忽略Z轴
        /// </summary>
        public static float DistanceXY(ref Vector3 a, ref Vector3 b)
        {
            float dx = a.x - b.x;
            float dy = a.y - b.y;
            return Mathf.Sqrt(dx * dx + dy * dy);
        }

        /// <summary>
        ///   计算平方距离
        /// </summary>
        public static float SqrDistance(ref Vector2 a, ref Vector2 b)
        {
            var dx = a.x - b.x;
            var dy = a.y - b.y;
            return dx * dx + dy * dy;
        }

        /// <summary>
        ///   计算平方距离
        /// </summary>
        public static float SqrDistance(ref Vector3 a, ref Vector3 b)
        {
            var dx = a.x - b.x;
            var dy = a.y - b.y;
            var dz = a.z - b.z;
            return dx * dx + dy * dy + dz * dz;
        }

        /// <summary>
        ///   计算平方距离
        /// </summary>
        public static float SqrDistance(Vector3 a, Vector3 b)
        {
            var dx = a.x - b.x;
            var dy = a.y - b.y;
            var dz = a.z - b.z;
            return dx * dx + dy * dy + dz * dz;
        }

        /// <summary>
        ///   计算平方距离，忽略Y轴
        /// </summary>
        public static float SqrDistanceXZ(ref Vector3 a, ref Vector3 b)
        {
            var dx = a.x - b.x;
            var dz = a.z - b.z;
            return dx * dx + dz * dz;
        }

        /// <summary>
        ///   计算平方距离，忽略Z轴
        /// </summary>
        public static float SqrDistanceXY(ref Vector3 a, ref Vector3 b)
        {
            var dx = a.x - b.x;
            var dy = a.y - b.y;
            return dx * dx + dy * dy;
        }

        /// <summary>
        ///   计算b是否在a，Math.Sqrt(distSq)范围以内, 忽略Y轴， 无开方运算
        /// </summary>
        public static bool WithinRangeSqrXZ(ref Vector3 a, ref Vector3 b, float distSq)
        {
            return SqrDistanceXZ(ref a, ref b) <= distSq;
        }

        /// <summary>
        ///   计算b是否在a，Math.Sqrt(distSq)范围以内, 忽略Z轴， 无开方运算
        /// </summary>
        public static bool WithinRangeSqrXY(ref Vector3 a, ref Vector3 b, float distSq)
        {
            return SqrDistanceXY(ref a, ref b) <= distSq;
        }

        /// <summary>
        ///   随机挑选一个元素
        /// </summary>
        public static T RandomPick<T>(T[] arr)
        {
            var n = null != arr ? arr.Length : 0;
            if (n > 0)
            {
                if (1 == n)
                    return arr[0];
                return arr[UnityEngine.Random.Range(0, n)];
            }
            return default(T);
        }

        /// <summary>
        ///   随机挑选一个元素
        /// </summary>
        public static T RandomSelect<T>(this T[] arr)
        {
            var n = null != arr ? arr.Length : 0;
            if (n > 0)
            {
                if (1 == n)
                    return arr[0];
                return arr[UnityEngine.Random.Range(0, n)];
            }
            return default(T);
        }

        /// <summary>
        ///   随机挑选一个元素
        /// </summary>
        public static T RandomPick<T>(List<T> list)
        {
            var n = null != list ? list.Count : 0;
            if (n > 0)
            {
                if (1 == n)
                    return list[0];
                return list[UnityEngine.Random.Range(0, n)];
            }
            return default(T);
        }

        public static bool RandomChoice<T>(List<T> source, out T result)
        {
            if (null == source || source.Count == 0)
            {
                result = default(T);
                return false;
            }
            result = source[UnityEngine.Random.Range(0, source.Count)];
            return true;
        }

        public static bool RandomSelect<T>(this List<T> source, out T result)
        {
            if (null == source || source.Count == 0)
            {
                result = default(T);
                return false;
            }
            int total = source.Count;
            if (1 == total)
            {
                result = source[0];
            }
            else
            {
                result = source[UnityEngine.Random.Range(0, total)];
            }
            return true;
        }

        public static bool RandomSelect<T>(this T[] source, out T result)
        {
            if (null == source || source.Length == 0)
            {
                result = default(T);
                return false;
            }
            int total = source.Length;
            if (1 == total)
            {
                result = source[0];
            }
            else
            {
                result = source[UnityEngine.Random.Range(0, source.Length)];
            }
            return true;
        }

        /// <summary>
        /// 四舍五入
        /// </summary>
        /// digits:保留几位小数
        public static float Round(float value, int digits = 1)
        {
            if (value == 0)
            {
                return 0;
            }
            float multiple = Mathf.Pow(10, digits);
            float tempValue = value > 0 ? value * multiple + 0.5f : value * multiple - 0.5f;
            tempValue = Mathf.FloorToInt(tempValue);
            return tempValue / multiple;
        }

        public static float3 BezierCurve(float3 p0, float3 p1, float3 p2, float t)
        {
            float u = 1 - t;
            float tt = t * t;
            float uu = u * u;
            float uuu = uu * u;
            float ttt = tt * t;

            float3 p = uuu * p0; // (1-t)^3 * p0
            p += 3 * uu * t * p1; // 3(1-t)^2 * t * p1
            p += 3 * u * tt * p2; // 3(1-t) * t^2 * p2
            p += ttt * p2;      // t^3 * p3
            return p;
        }

        #region 从序列中随机选择n个元素算法(支持局部挑选)

        public static void RandomChoice<T>(List<T> source, int n, List<T> output, int startIndex = 0, int count = -1)
        {
            // if (n > source.Count)
            //     n = source.Count;
            // // TODO: 算法优化
            // var buff = new List<T>(source);
            // int count = 0;
            // while(count < n && buff.Count > 0)
            // {
            //     int choice = Random.Range(0, buff.Count);
            //     output.Add(buff[choice]);
            //     buff.RemoveAt(choice);
            //     count++;
            // }
            throw new System.NotImplementedException();
        }

        public static void RandomChoice<T>(T[] source, int n, T[] output, int startIndex = 0, int count = -1)
        {
            // if (n > source.Length)
            //     n = source.Length;
            // if (output.Length < n)
            //     return;
            throw new System.NotImplementedException();
        }

        #endregion

        #region 洗牌算法

        // https://developerslogblog.wordpress.com/2020/02/04/how-to-shuffle-an-array/
        public static void Shuffle<T>(this T[] inputArray)
        {
            System.Random random = new System.Random(System.Guid.NewGuid().GetHashCode());
            for (int i = inputArray.Length - 1; i > 0; i--)
            {
                int randomIndex = random.Next(0, i + 1);

                var temp = inputArray[i];
                inputArray[i] = inputArray[randomIndex];
                inputArray[randomIndex] = temp;
            }
        }

        public static void Shuffle<T>(this List<T> inputArray)
        {
            System.Random random = new System.Random(System.Guid.NewGuid().GetHashCode());
            for (int i = inputArray.Count - 1; i > 0; i--)
            {
                int randomIndex = random.Next(0, i + 1);

                var temp = inputArray[i];
                inputArray[i] = inputArray[randomIndex];
                inputArray[randomIndex] = temp;
            }
        }
        #endregion

    }
}
